package com.prim.update;

import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.DrawableRes;
import android.text.TextUtils;
import android.util.Log;

import com.prim.update.http.UpdateHttpClient;
import com.prim.update.impl.UpdateExplorer;
import com.prim.update.interfaces.IUpdateChecker;
import com.prim.update.interfaces.IUpdateConverter;
import com.prim.update.interfaces.IUpdateDownloader;
import com.prim.update.interfaces.IUpdateFile;
import com.prim.update.interfaces.IUpdateLogger;
import com.prim.update.interfaces.IUpdatePlan;
import com.prim.update.interfaces.IUpdatePrompter;
import com.prim.update.listener.OnDownloadListener;
import com.prim.update.model.Progress;
import com.prim.update.state.StateType;
import com.prim.update.state.UpdateState;
import com.prim.update.utils.Utils;

import java.io.File;
import java.lang.ref.WeakReference;
import java.util.LinkedHashMap;

/**
 * ================================================
 * 作    者：linksus
 * 版    本：1.0
 * 创建日期：3/5 0005
 * 描    述：
 * 版本更新的管理者:决定事情的方向,管理者只需要知道任务的执行度和完成情况.
 * 具体如何实现交给总管去分配和督查所有任务{@link UpdateExplorer} 将任务情况报告给管理者
 * 修订历史：
 * ================================================
 */
public class UpdateManager implements OnDownloadListener {
    public static long REFRESH_TIME = 300;                      //回调刷新时间（单位ms）
    private static final String TAG = "UpdateManager";
    //用于在主线程执行的调度器
    private Handler mHandler;
    private UpdateHttpClient httpClient;
    private static UpdateExplorer explorer;

    private OnDownloadProgressListener listener;

    /** 监听下载进度 */
    public void setOnDownloadProgressListener(OnDownloadProgressListener listener) {
        this.listener = listener;
    }

    public interface OnDownloadProgressListener {
        void onStart();

        void onProgress(Progress progress);

        void onError(String msg);

        void onFinish();
    }

    @Override
    public void onStart() {
        if (listener != null) {
            listener.onStart();
        }
    }

    @Override
    public void onProgress(Progress progress) {
        if (listener != null)
            listener.onProgress(progress);
    }

    @Override
    public void onError(String msg) {
        if (listener != null)
            listener.onError(msg);
    }

    @Override
    public void onFinish() {
        if (listener != null)
            listener.onFinish();
    }

    private static class updateHolder {
        private static UpdateManager holder = new UpdateManager();
    }

    public static UpdateManager getInstance() {
        return updateHolder.holder;
    }

    UpdateManager() {
        mHandler = new Handler(Looper.getMainLooper());
    }

    /** 必须设置网络客户端 实现UpdateHttpClient接口 */
    public UpdateManager setHttpClient(UpdateHttpClient httpClient) {
        check();
        this.httpClient = httpClient;
        return this;
    }

    private void check() {
        if (explorer != null && explorer.getBuilder().isDownloadAgain) {
            if (checkTaskState() == StateType.DOWNLOAD) {
                explorer.stop();
                if (getHttpClient() != null) {
                    getHttpClient().cancleDownload();
                }
            }
        }
    }

    public boolean deleteApkFile() {
        return explorer != null && explorer.deleteApkFile();
    }

    public void cancelPrompterTime() {
        if (explorer != null) explorer.cancelPrompterTime();
    }

    public void cancelIgnorable() {
        if (explorer != null) explorer.cancelIgnorable();
    }

    /** 获取网络客户端 */
    public UpdateHttpClient getHttpClient() {
        Utils.checkNotNull(httpClient, "httpClient is null");
        return httpClient;
    }

    /** 在主线程中运行 */
    public void runOnUiThread(Runnable runnable) {
        mHandler.post(runnable);
    }

    /** 检查任务的执行状态 */
    public StateType checkTaskState() {
        return UpdateState.getState();
    }

    /** 管理者创建决策 */
    public Builder create(Context context) {
        Utils.checkNotNull(httpClient, "httpClient is null ,please init httpClient");
        return new Builder(context);
    }

    public static class Builder {
        private WeakReference<Context> mContext;

        /** 下载的文件存储路径 */
        public String storagePath;

        /** 下载过程中是否在通知栏上显示下载进度 */
        public boolean isNotificationShow = true;

        /** 通知栏的id */
        public int notificationId = -1;

        /** 通知栏上显示的大图标 */
        public int largeIcon;

        /** 通知栏上显示的小图标 */
        public int smallIcon;

        /** 是否仅WiFi的是否下载 */
        public boolean isWifiOnly;

        /** 不是WiFi的网络下是否提示用户 下载将耗费流量 */
        public boolean isNotWifiPrompter;

        /** 请求服务器检测版本是否更新的URL */
        public String checkUrl;

        /** 请求所带的参数 */
        public LinkedHashMap<String, String> params;

        /** 请求类型 */
        public RequestType type = RequestType.GET;

        /** 用户点击取消更新后-是否在后台静默自动下载 */
        public boolean isBackstage;

        /** 检查到新版本不需要任务UI提示在后台静默自动下载 */
        public boolean isAutoDownload;

        /** 下载完成后时候是否自动安装 */
        public boolean isAutoInstall;

        /** 是否显示下载进度弹窗 */
        public boolean isShowProgressDialog;

        /** 设置下载的标题 */
        public String downloadTitle;

        /** 是否强制重新下载 不管安装包是否存在,或者在后台静默下载,全部删除重新下载 */
        public boolean isDownloadAgain;

        /**
         * 手动设置是否强制更新,也可以由服务器传来的数据判断
         * 规则:手动设置优先级高于服务器设置
         * 规则可以自己在ui提示中进行修改 {@link com.prim.update.impl.DefultUpdatePrompterUIHandler}
         */
        public boolean isEnforce;

        /**
         * 更新或安装apk手动设置是否可以忽略该版本,可以不更新
         * 也可以由服务器传来的数据进行判断
         * 规则可以自己在ui提示中进行修改{@link com.prim.update.impl.DefultUpdatePrompterUIHandler}
         */
        public boolean isIgnorable;

        /** 手动设置更新UI的提示周期时间 注意这里以天为单位 */
        public int prompterTime = -1;

        /**
         * 更新UI提示的优先级 false - 服务器优先 true - 手动设置的优先,若没有手动设置提示周期
         * 既prompterTime = -1 以服务器的提示周期为准
         */
        public boolean prompterPriority;

        /** 是否开启周期提示，若为FALSE 不管是服务器还是手动设置的周期时间都无效 */
        public boolean isCycleUpdatePrompter;

        /** 是否开启增量更新 */
        public boolean isPatchUpdate;

        /** 一下这几个是任务岗位 由管理者决策谁去胜任这些岗位 */
        private IUpdateChecker updateChecker;
        private IUpdateConverter updateConverter;
        private IUpdatePlan updatePlan;
        private IUpdatePrompter updatePrompter;
        private IUpdateDownloader updateDownloader;
        private IUpdateFile updateFile;
        private IUpdateLogger updateLogger;
        private OnDownloadListener downloadUIHandler;
        private OnDownloadListener downloadUINotificationHandler;

        public enum RequestType {
            POST, GET
        }

        public Builder isPatchUpdate(boolean isPatchUpdate) {
            this.isPatchUpdate = isPatchUpdate;
            return this;
        }

        public Builder isCycleUpdatePrompter(boolean isCyclePrompter) {
            this.isCycleUpdatePrompter = isCyclePrompter;
            return this;
        }

        public Builder setPrompterTime(int prompterTime) {
            this.prompterTime = prompterTime;
            return this;
        }

        public Builder prompterPriority(boolean prompterPriority) {
            this.prompterPriority = prompterPriority;
            return this;
        }

        public Builder isIgnorable(boolean isIgnorable) {
            this.isIgnorable = isIgnorable;
            return this;
        }

        public Builder isEnforce(boolean isEnforce) {
            this.isEnforce = isEnforce;
            return this;
        }

        public Builder isDownloadAgain(boolean isDownloadAgain) {
            this.isDownloadAgain = isDownloadAgain;
            return this;
        }

        public Builder setDownloadTitle(String title) {
            this.downloadTitle = title;
            return this;
        }

        public Builder isAutoDownload(boolean isAutoDownload) {
            this.isAutoDownload = isAutoDownload;
            return this;
        }

        public Builder isNotWifiPrompter(boolean isNotWifiPrompter) {
            this.isNotWifiPrompter = isNotWifiPrompter;
            return this;
        }

        public Builder isShowProgressDialog(boolean isShowProgressDialog) {
            this.isShowProgressDialog = isShowProgressDialog;
            return this;
        }

        public Builder isAutoInstall(boolean isAutoInstall) {
            this.isAutoInstall = isAutoInstall;
            return this;
        }

        public Builder isBackstage(boolean isBackstage) {
            this.isBackstage = isBackstage;
            return this;
        }

        public Builder setNotificationId(int notificationId) {
            this.notificationId = notificationId;
            return this;
        }

        public Builder setRequestType(RequestType type) {
            this.type = type;
            return this;
        }

        public Builder setParams(LinkedHashMap<String, String> params) {
            this.params.putAll(params);
            return this;
        }

        public Builder setParams(String key, String value) {
            this.params.put(key, value);
            return this;
        }

        public Builder setCheckUrl(String checkUrl) {
            this.checkUrl = checkUrl;
            return this;
        }

        public Builder isWifiOnly(boolean isWifiOnly) {
            this.isWifiOnly = isWifiOnly;
            return this;
        }

        public Builder setStoragePath(String storagePath) {
            this.storagePath = storagePath;
            return this;
        }

        public Builder isNotificationShow(boolean isNotificationShow) {
            this.isNotificationShow = isNotificationShow;
            return this;
        }

        public Builder setNotificationLargeIcon(@DrawableRes int largeIcon) {
            this.largeIcon = largeIcon;
            return this;
        }

        public Builder setNotificationSmallIcon(@DrawableRes int smallIcon) {
            this.smallIcon = smallIcon;
            return this;
        }

        public Builder setUpdateChecker(IUpdateChecker updateChecker) {
            this.updateChecker = updateChecker;
            return this;
        }

        public Builder setUpdateConverter(IUpdateConverter updateConverter) {
            this.updateConverter = updateConverter;
            return this;
        }

        public Builder setUpdatePlan(IUpdatePlan updatePlan) {
            this.updatePlan = updatePlan;
            return this;
        }

        public Builder setUpdatePrompter(IUpdatePrompter updatePrompter) {
            this.updatePrompter = updatePrompter;
            return this;
        }

        public Builder setUpdateDownloader(IUpdateDownloader updateDownloader) {
            this.updateDownloader = updateDownloader;
            return this;
        }

        public Builder setUpdateFile(IUpdateFile updateFile) {
            this.updateFile = updateFile;
            return this;
        }

        public Builder setUpdateLogger(IUpdateLogger updateLogger) {
            this.updateLogger = updateLogger;
            return this;
        }

        public Builder setDownloadUIHandler(OnDownloadListener downloadUIHandler) {
            this.downloadUIHandler = downloadUIHandler;
            return this;
        }

        public Builder setDownloadUINotificationHandler(OnDownloadListener downloadUINotificationHandler) {
            this.downloadUINotificationHandler = downloadUINotificationHandler;
            return this;
        }

        /** 管理者创建任务 */
        Builder(Context context) {
            mContext = new WeakReference<>(context);
            params = new LinkedHashMap<>();
            Utils.init(context);
        }


        /** 管理者检查任务 */
        public void check() {
            /* 如果任务不是终止状态 */
            if (UpdateState.getState() != StateType.STOP) {
                Log.e(TAG, "check: " + UpdateState.getState());
                if (UpdateState.getState() == StateType.DOWNLOAD) {
                    if (explorer != null) explorer.check();
                } else {
                    //强制终止任务
                    UpdateState.setState(StateType.STOP);
                }
                return;
            }
            /* 请求的更新地址为空任务终止 */
            if (TextUtils.isEmpty(checkUrl)) {
                UpdateState.setState(StateType.STOP);
                return;
            }
            startTask();
        }

        /** 管理者检查任务完毕,决定任务进入开始阶段 */
        private void startTask() {
            if (explorer != null) explorer = null;
            UpdateState.setState(StateType.CREATE);
            /* 管理者将任务决策交给总管去执行 */
            explorer = new UpdateExplorer(mContext, this);
            /* 总管开始安排任务 */
            // 1. 将这些人带到岗位中去替换默认的实现者
            if (updateChecker != null) explorer.setUpdateChecker(updateChecker);
            if (updateConverter != null) explorer.setUpdateConverter(updateConverter);
            if (updatePrompter != null) explorer.setUpdatePrompter(updatePrompter);
            if (updateDownloader != null) explorer.setUpdateDownloader(updateDownloader);
            if (updateFile != null) explorer.setUpdateFile(updateFile);
            if (updateLogger != null) explorer.setUpdateLogger(updateLogger);
            if (updatePlan != null) explorer.setUpdatePlan(updatePlan);
            if (downloadUINotificationHandler != null)
                explorer.setDownloadUINotificationHandler(downloadUINotificationHandler);
            if (downloadUIHandler != null) explorer.setDownloadUIHandler(downloadUIHandler);
            // 2. 开始任务
            explorer.check();
        }
    }
}
